package client

import (
	"crypto/rand"
	"crypto/rsa"
	"crypto/x509"
	"crypto/x509/pkix"
	"encoding/pem"
	"errors"
	"github.com/gorilla/go-simplejson"
	"math/big"
	"net"
	"os"
	"runtime"
	"strings"
	"time"
)

// Match pattern with msg
func PatternMatch(pattern *json.Json, msg *json.Json) bool {
	m, err := msg.Map()
	if err != nil {
		return false
	}

	arr, err := pattern.Array()
	if err == nil {
		for _, element := range arr {
			if patternMatch(element, m) {
				return true
			}
		}
	} else {
		p, err := pattern.Map()
		if err != nil {
			return false
		}
		if patternMatch(p, m) {
			return true
		}
	}
	return false
}

func patternMatch(pattern, msg interface{}) bool {
	_p := pattern.(map[string]interface{})
	_m := msg.(map[string]interface{})
	for k, v := range _p {
		_pv, ok := _m[k]
		if !ok {
			return false
		}
		if _, vok := v.(map[string]interface{}); vok {
			if _, pvok := _pv.(map[string]interface{}); !pvok {
				return false
			}
			if !patternMatch(v, _pv) {
				return false
			}
		} else {
			if _pv != v {
				return false
			}
		}
	}
	return true
}

// Validate a package
func Validate(j string) (*Pack, error) {
	m, err := json.NewJson([]byte(j))
	if err != nil {
		return nil, err
	}
	if !m.Has("id") {
		return nil, errors.New("no id found")
	}
	if !m.Has("cmd") {
		return nil, errors.New("no cmd found")
	}
	if !m.Has("msg") {
		return nil, errors.New("no msg found")
	}
	return NewPackWithJSON(m), nil
}

// Translate JSON to string
func JSONToString(p *json.Json) string {
	s, _ := p.MarshalJSON()
	return string(s)
}

func GenerateCert(cert_path, key_path string) {
	host := "127.0.0.1"
	validFor := 365 * 24 * time.Hour
	rsaBits := 2048
	notBefore := time.Now()
	notAfter := notBefore.Add(validFor)
	endOfTime := time.Date(2049, 12, 31, 23, 59, 59, 0, time.UTC)
	if notAfter.After(endOfTime) {
		notAfter = endOfTime
	}

	priv, err := rsa.GenerateKey(rand.Reader, rsaBits)
	if err != nil {
		return
	}

	template := x509.Certificate{
		SerialNumber: new(big.Int).SetInt64(0),
		Subject: pkix.Name{
			Organization: []string{"Acme Co"},
		},
		NotBefore:             notBefore,
		NotAfter:              notAfter,
		KeyUsage:              x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature,
		ExtKeyUsage:           []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
		BasicConstraintsValid: true,
	}

	hosts := strings.Split(host, ",")
	for _, h := range hosts {
		if ip := net.ParseIP(h); ip != nil {
			template.IPAddresses = append(template.IPAddresses, ip)
		} else {
			template.DNSNames = append(template.DNSNames, h)
		}
	}

	derBytes, err := x509.CreateCertificate(rand.Reader, &template, &template, &priv.PublicKey, priv)
	if err != nil {
		return
	}

	certOut, err := os.Create(cert_path)
	if err != nil {
		return
	}
	pem.Encode(certOut, &pem.Block{Type: "CERTIFICATE", Bytes: derBytes})
	certOut.Close()

	keyOut, err := os.OpenFile(key_path, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600)
	if err != nil {
		return
	}
	pem.Encode(keyOut, &pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(priv)})
	keyOut.Close()
}
